package org.kiwi.data.config;

import org.kiwi.data.annotation.DbShardingStrategyName;
import org.kiwi.data.interceptor.DbShardingInterceptor;
import org.kiwi.data.sharding.ClassPathShardingStrategyScanner;
import org.kiwi.data.util.AopUtil;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;

import static org.kiwi.data.cons.DataConstant.*;
import static org.kiwi.data.util.ShardingUtil.scan;

/**
 * Created by jack08.liu on 2016/12/21.
 */
public class DbShardingBeanDefinitionParser implements BeanDefinitionParser {

    private ClassPathShardingStrategyScanner classPathDbShardingStrategyScanner;

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        addDbShardingStrategyNameAnnotationInClassPathScanner(parserContext);

        registerDbShardingInterceptor(parserContext, element.getAttribute(ATTRIBUTE_STRATEGY_SCAN_PACKAGE));

        registerDbShardingAopConfig(element, parserContext, element.getAttribute(ATTRIBUTE_POINTCUT_EXPRESSION));

        return null;
    }

    private void addDbShardingStrategyNameAnnotationInClassPathScanner(ParserContext parserContext) {
        this.classPathDbShardingStrategyScanner = new ClassPathShardingStrategyScanner(parserContext.getRegistry());
        this.classPathDbShardingStrategyScanner.setShardingStrategyClass(DbShardingStrategyName.class);
        this.classPathDbShardingStrategyScanner.registerFilters();
    }

    private void registerDbShardingInterceptor(ParserContext parserContext, String strategyPackage) {
        RootBeanDefinition dbShardingInterceptor = new RootBeanDefinition(DbShardingInterceptor.class);
        ManagedMap<String, BeanDefinitionHolder> shardingStrategies = scan(this.classPathDbShardingStrategyScanner, strategyPackage);
        dbShardingInterceptor.getPropertyValues().add(DB_SHARDING_STRATEGIES, shardingStrategies);
        parserContext.getRegistry().registerBeanDefinition(DB_SHARDING_INTERCEPTOR, dbShardingInterceptor);
    }

    private void registerDbShardingAopConfig(Element element, ParserContext parserContext, String pointCutExpression) {
        AopUtil.registerAopConfig(
                element,
                parserContext,
                DB_SHARDING_POINTCUT,
                pointCutExpression,
                DB_SHARDING_INTERCEPTOR,
                DB_SHARDING_ADVICE_ORDER
        );
    }

}
